<template>
  <div>
    <div id="upload" class="modal" data-state="0" data-ready="false">
      <div class="modal__header">
        <button class="modal__close-button" type="button">
          <svg
            class="modal__close-icon"
            viewBox="0 0 16 16"
            width="16px"
            height="16px"
            aria-hidden="true"
          >
            <g
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
            >
              <polyline points="1,1 15,15" />
              <polyline points="15,1 1,15" />
            </g>
          </svg>
          <span class="modal__sr">Close</span>
        </button>
      </div>
      <div class="modal__body">
        <div class="modal__col">
          <!-- up -->
          <svg
            class="modal__icon modal__icon--blue"
            viewBox="0 0 24 24"
            width="24px"
            height="24px"
            aria-hidden="true"
          >
            <g
              fill="none"
              stroke="hsl(223,90%,50%)"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle
                class="modal__icon-sdo69"
                cx="12"
                cy="12"
                r="11"
                stroke-dasharray="69.12 69.12"
              />
              <polyline
                class="modal__icon-sdo14"
                points="7 12 12 7 17 12"
                stroke-dasharray="14.2 14.2"
              />
              <line
                class="modal__icon-sdo10"
                x1="12"
                y1="7"
                x2="12"
                y2="17"
                stroke-dasharray="10 10"
              />
            </g>
          </svg>
          <!-- error -->
          <svg
            class="modal__icon modal__icon--red"
            viewBox="0 0 24 24"
            width="24px"
            height="24px"
            aria-hidden="true"
            display="none"
          >
            <g
              fill="none"
              stroke="hsl(3,90%,50%)"
              stroke-width="2"
              stroke-linecap="round"
            >
              <circle
                class="modal__icon-sdo69"
                cx="12"
                cy="12"
                r="11"
                stroke-dasharray="69.12 69.12"
              />
              <line
                class="modal__icon-sdo14"
                x1="7"
                y1="7"
                x2="17"
                y2="17"
                stroke-dasharray="14.2 14.2"
              />
              <line
                class="modal__icon-sdo14"
                x1="17"
                y1="7"
                x2="7"
                y2="17"
                stroke-dasharray="14.2 14.2"
              />
            </g>
          </svg>
          <!-- check -->
          <svg
            class="modal__icon modal__icon--green"
            viewBox="0 0 24 24"
            width="24px"
            height="24px"
            aria-hidden="true"
            display="none"
          >
            <g
              fill="none"
              stroke="hsl(138,90%,50%)"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle
                class="modal__icon-sdo69"
                cx="12"
                cy="12"
                r="11"
                stroke-dasharray="69.12 69.12"
              />
              <polyline
                class="modal__icon-sdo14"
                points="7 12.5 10 15.5 17 8.5"
                stroke-dasharray="14.2 14.2"
              />
            </g>
          </svg>
        </div>
        <div class="modal__col">
          <div class="modal__content">
            <h2 class="modal__title">Upload a File</h2>
            <p class="modal__message">
              Select a file to upload from your computer or device.
            </p>
            <div class="modal__actions">
              <button
                class="modal__button modal__button--upload"
                type="button"
                data-action="file"
              >
                Choose File
              </button>
              <input id="file" type="file" hidden />
            </div>
            <div class="modal__actions" hidden>
              <svg
                class="modal__file-icon"
                viewBox="0 0 24 24"
                width="24px"
                height="24px"
                aria-hidden="true"
              >
                <g
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                >
                  <polygon points="4 1 12 1 20 8 20 23 4 23" />
                  <polyline points="12 1 12 8 20 8" />
                </g>
              </svg>
              <div class="modal__file" data-file></div>
              <button
                class="modal__close-button"
                type="button"
                data-action="fileReset"
              >
                <svg
                  class="modal__close-icon"
                  viewBox="0 0 16 16"
                  width="16px"
                  height="16px"
                  aria-hidden="true"
                >
                  <g
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                  >
                    <polyline points="4,4 12,12" />
                    <polyline points="12,4 4,12" />
                  </g>
                </svg>
                <span class="modal__sr">Remove</span>
              </button>
              <button class="modal__button" type="button" data-action="upload">
                Upload
              </button>
            </div>
          </div>
          <div class="modal__content" hidden>
            <h2 class="modal__title">Uploading…</h2>
            <p class="modal__message">
              Just give us a moment to process your file.
            </p>
            <div class="modal__actions">
              <div class="modal__progress">
                <div class="modal__progress-value" data-progress-value>0%</div>
                <div class="modal__progress-bar">
                  <div class="modal__progress-fill" data-progress-fill></div>
                </div>
              </div>
              <button class="modal__button" type="button" data-action="cancel">
                Cancel
              </button>
            </div>
          </div>
          <div class="modal__content" hidden>
            <h2 class="modal__title">Oops!</h2>
            <p class="modal__message">
              Your file could not be uploaded due to an error. Try uploading it
              again?
            </p>
            <div class="modal__actions modal__actions--center">
              <button class="modal__button" type="button" data-action="upload">
                Retry
              </button>
              <button class="modal__button" type="button" data-action="cancel">
                Cancel
              </button>
            </div>
          </div>
          <div class="modal__content" hidden>
            <h2 class="modal__title">Upload Successful!</h2>
            <p class="modal__message">
              Your file has been uploaded. You can copy the link to your
              clipboard.
            </p>
            <div class="modal__actions modal__actions--center">
              <button class="modal__button" type="button" data-action="copy">
                Copy Link
              </button>
              <button class="modal__button" type="button" data-action="cancel">
                Done
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {};
  },
  mounted() {
    this.init();
  },
  computed: {},
  methods: {
    init() {
      window.addEventListener("DOMContentLoaded", () => {
        const upload = new UploadModal("#upload");
      });

      class UploadModal {
        filename = "";
        isCopying = false;
        isUploading = false;
        progress = 0;
        progressTimeout = null;
        state = 0;

        constructor(el) {
          this.el = document.querySelector(el);
          this.el?.addEventListener("click", this.action.bind(this));
          this.el
            ?.querySelector("#file")
            ?.addEventListener("change", this.fileHandle.bind(this));
        }
        action(e) {
          this[e.target?.getAttribute("data-action")]?.();
          this.stateDisplay();
        }
        cancel() {
          this.isUploading = false;
          this.progress = 0;
          this.progressTimeout = null;
          this.state = 0;
          this.stateDisplay();
          this.progressDisplay();
          this.fileReset();
        }
        async copy() {
          const copyButton = this.el?.querySelector("[data-action='copy']");

          if (!this.isCopying && copyButton) {
            // disable
            this.isCopying = true;
            copyButton.style.width = `${copyButton.offsetWidth}px`;
            copyButton.disabled = true;
            copyButton.textContent = "Copied!";
            navigator.clipboard.writeText(this.filename);
            await new Promise((res) => setTimeout(res, 1000));
            // reenable
            this.isCopying = false;
            copyButton.removeAttribute("style");
            copyButton.disabled = false;
            copyButton.textContent = "Copy Link";
          }
        }
        fail() {
          this.isUploading = false;
          this.progress = 0;
          this.progressTimeout = null;
          this.state = 2;
          this.stateDisplay();
        }
        file() {
          this.el?.querySelector("#file").click();
        }
        fileDisplay(name = "") {
          // update the name
          this.filename = name;

          const fileValue = this.el?.querySelector("[data-file]");
          if (fileValue) fileValue.textContent = this.filename;

          // show the file
          this.el?.setAttribute("data-ready", this.filename ? "true" : "false");
        }
        fileHandle(e) {
          return new Promise(() => {
            const { target } = e;
            if (target?.files.length) {
              let reader = new FileReader();
              reader.onload = (e2) => {
                this.fileDisplay(target.files[0].name);
              };
              reader.readAsDataURL(target.files[0]);
            }
          });
        }
        fileReset() {
          const fileField = this.el?.querySelector("#file");
          if (fileField) fileField.value = null;

          this.fileDisplay();
        }
        progressDisplay() {
          const progressValue = this.el?.querySelector("[data-progress-value]");
          const progressFill = this.el?.querySelector("[data-progress-fill]");
          const progressTimes100 = Math.floor(this.progress * 100);

          if (progressValue) progressValue.textContent = `${progressTimes100}%`;
          if (progressFill)
            progressFill.style.transform = `translateX(${progressTimes100}%)`;
        }
        async progressLoop() {
          this.progressDisplay();

          if (this.isUploading) {
            if (this.progress === 0) {
              await new Promise((res) => setTimeout(res, 1000));
              // fail randomly
              if (!this.isUploading) {
                return;
              } else if (Utils.randomInt(0, 2) === 0) {
                this.fail();
                return;
              }
            }
            // …or continue with progress
            if (this.progress < 1) {
              this.progress += 0.01;
              this.progressTimeout = setTimeout(
                this.progressLoop.bind(this),
                50
              );
            } else if (this.progress >= 1) {
              this.progressTimeout = setTimeout(() => {
                if (this.isUploading) {
                  this.success();
                  this.stateDisplay();
                  this.progressTimeout = null;
                }
              }, 250);
            }
          }
        }
        stateDisplay() {
          this.el?.setAttribute("data-state", `${this.state}`);
        }
        success() {
          this.isUploading = false;
          this.state = 3;
          this.stateDisplay();
        }
        upload() {
          if (!this.isUploading) {
            this.isUploading = true;
            this.progress = 0;
            this.state = 1;
            this.progressLoop();
          }
        }
      }

      class Utils {
        static randomInt(min = 0, max = 2 ** 32) {
          const percent =
            crypto.getRandomValues(new Uint32Array(1))[0] / 2 ** 32;
          const relativeValue = (max - min) * percent;

          return Math.round(min + relativeValue);
        }
      }
    },
  },
};
</script>
<style scoped>
* {
  border: 0;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
:root {
  --hue: 223;
  --bg: hsl(var(--hue), 10%, 85%);
  --fg: hsl(var(--hue), 10%, 5%);
  --trans-dur: 0.3s;
  font-size: calc(16px + (20 - 16) * (100vw - 320px) / (2560 - 320));
}
body,
button {
  font: 1em/1.5 Nunito, Helvetica, sans-serif;
}
body {
  background-color: var(--bg);
  color: var(--fg);
  height: 100vh;
  display: grid;
  place-items: center;
  transition: background-color var(--trans-dur), color var(--trans-dur);
}
.modal {
  background-color: hsl(var(--hue), 10%, 95%);
  border-radius: 1em;
  box-shadow: 0 0.75em 1em hsla(var(--hue), 10%, 5%, 0.3);
  color: hsl(var(--hue), 10%, 5%);
  width: calc(100% - 3em);
  max-width: 34.5em;
  overflow: hidden;
  position: relative;
  transition: background-color var(--trans-dur), color var(--trans-dur);
}
.modal:before {
  background-color: hsl(223, 90%, 60%);
  border-radius: 50%;
  content: "";
  filter: blur(60px);
  opacity: 0.15;
  position: absolute;
  top: -8em;
  right: -15em;
  width: 25em;
  height: 25em;
  z-index: 0;
  transition: background-color var(--trans-dur);
}
.modal__actions {
  animation-delay: 0.2s;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.modal__body,
.modal__header {
  position: relative;
  z-index: 1;
}
.modal__body {
  display: flex;
  flex-direction: column;
  padding: 0 2em 1.875em 1.875em;
}
.modal__button,
.modal__close-button {
  color: currentColor;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.modal__button {
  background-color: hsla(var(--hue), 10%, 50%, 0.2);
  border-radius: 0.25rem;
  font-size: 0.75em;
  padding: 0.5rem 2rem;
  transition: background-color var(--trans-dur), border-color var(--trans-dur),
    opacity var(--trans-dur);
  width: 100%;
}
.modal__button + .modal__button {
  margin-top: 0.75em;
}
.modal__button:disabled {
  opacity: 0.5;
}
.modal__button:focus,
.modal__close-button:focus {
  outline: transparent;
}
.modal__button:hover,
.modal__button:focus-visible {
  background-color: hsla(var(--hue), 10%, 60%, 0.2);
}
.modal__button--upload {
  background-color: transparent;
  border: 0.125rem dashed hsla(var(--hue), 10%, 50%, 0.4);
  flex: 1;
  padding: 0.375rem 2rem;
}
.modal__col + .modal__col {
  flex: 1;
  margin-top: 1.875em;
}
.modal__close-button,
.modal__message,
.modal__progress-value {
  color: hsl(var(--hue), 10%, 30%);
  transition: color var(--trans-dur);
}
.modal__close-button {
  background-color: transparent;
  display: flex;
  width: 1.5em;
  height: 1.5em;
  transition: color var(--trans-dur);
}
.modal__close-button:hover,
.modal__close-button:focus-visible {
  color: hsl(var(--hue), 10%, 40%);
}
.modal__close-icon {
  display: block;
  margin: auto;
  pointer-events: none;
  width: 50%;
  height: auto;
}
.modal__content > * {
  /* don’t use shorthand syntax, or actions delay will be overridden */
  animation-name: fadeSlideIn;
  animation-duration: 0.5s;
  animation-timing-function: ease-in-out;
  animation-fill-mode: forwards;
  opacity: 0;
}
.modal__file {
  flex: 1;
  font-size: 0.75em;
  font-weight: 700;
  margin-right: 0.25rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.modal__file ~ .modal__button {
  margin-top: 1.5em;
}
.modal__file-icon {
  color: hsl(var(--hue), 10%, 50%);
  display: block;
  margin-right: 0.75em;
  width: 1.5em;
  height: 1.5em;
  transition: color var(--trans-dur);
}
.modal__header {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  height: 2.5em;
  padding: 0.5em;
}
.modal__icon {
  display: block;
  margin: auto;
  width: 2.25em;
  height: 2.25em;
}
.modal__icon--blue g {
  stroke: hsl(223, 90%, 50%);
}
.modal__icon--red g {
  stroke: hsl(3, 90%, 50%);
}
.modal__icon--green g {
  stroke: hsl(138, 90%, 40%);
}
.modal__icon circle,
.modal__icon line,
.modal__icon polyline {
  animation: sdo 0.25s ease-in-out forwards;
  transition: stroke var(--trans-dur);
}
.modal__icon :nth-child(2) {
  animation-delay: 0.25s;
}
.modal__icon :nth-child(3) {
  animation-delay: 0.5s;
}
.modal__icon-sdo10 {
  stroke-dashoffset: 10;
}
.modal__icon-sdo14 {
  stroke-dashoffset: 14.2;
}
.modal__icon-sdo69 {
  stroke-dashoffset: 69.12;
  transform: rotate(-90deg);
  transform-origin: 12px 12px;
}
.modal__message {
  animation-delay: 0.1s;
  font-size: 1em;
  margin-bottom: 1.5em;
  min-height: 3em;
}
.modal__progress {
  flex: 1;
}
.modal__progress + .modal__button {
  margin-top: 1.75em;
}
.modal__progress-bar {
  background-image: linear-gradient(
    90deg,
    hsl(var(--hue), 90%, 50%),
    hsl(var(--hue), 90%, 70%)
  );
  border-radius: 0.2em;
  overflow: hidden;
  width: 100%;
  height: 0.4em;
  transform: translate3d(0, 0, 0);
}
.modal__progress-fill {
  background-color: hsl(var(--hue), 10%, 90%);
  width: inherit;
  height: inherit;
  transition: transform 0.1s ease-in-out;
}
.modal__progress-value {
  font-size: 0.75em;
  font-weight: 700;
  line-height: 1.333;
  text-align: right;
}
.modal__sr {
  overflow: hidden;
  position: absolute;
  width: 1px;
  height: 1px;
}
.modal__title {
  font-size: 1.25em;
  font-weight: 500;
  line-height: 1.2;
  margin-bottom: 1.5rem;
  text-align: center;
}
/* state change */
[data-state="2"]:before {
  background-color: hsl(3, 90%, 60%);
}
[data-state="3"]:before {
  background-color: hsl(138, 90%, 60%);
}
.modal__icon + .modal__icon,
[data-state="1"] .modal__icon:first-child,
[data-state="2"] .modal__icon:first-child,
[data-state="3"] .modal__icon:first-child,
.modal__content + .modal__content,
[data-state="1"] .modal__content:first-child,
[data-state="2"] .modal__content:first-child,
[data-state="3"] .modal__content:first-child {
  display: none;
}
[data-state="1"] .modal__icon:first-child,
[data-state="2"] .modal__icon:nth-child(2),
[data-state="3"] .modal__icon:nth-child(3),
[data-state="1"] .modal__content:nth-child(2),
[data-state="2"] .modal__content:nth-child(3),
[data-state="3"] .modal__content:nth-child(4) {
  display: block;
}
[data-ready="false"] .modal__content:first-child .modal__actions:nth-of-type(2),
[data-ready="true"] .modal__content:first-child .modal__actions:first-of-type {
  display: none;
}
[data-ready="true"] .modal__content:first-child .modal__actions:nth-of-type(2) {
  display: flex;
}

/* Dark theme */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: hsl(var(--hue), 10%, 35%);
    --fg: hsl(var(--hue), 10%, 95%);
  }
  .modal {
    background-color: hsl(var(--hue), 10%, 10%);
    color: hsl(var(--hue), 10%, 95%);
  }
  .modal__close-button,
  .modal__message,
  .modal__progress-value {
    color: hsl(var(--hue), 10%, 70%);
  }
  .modal__close-button:hover,
  .modal__close-button:focus-visible {
    color: hsl(var(--hue), 10%, 80%);
  }
  .modal__file-icon {
    color: hsl(var(--hue), 10%, 60%);
  }
  .modal__icon--blue g {
    stroke: hsl(223, 90%, 60%);
  }
  .modal__icon--red g {
    stroke: hsl(3, 90%, 60%);
  }
  .modal__icon--green g {
    stroke: hsl(138, 90%, 60%);
  }
  .modal__progress-fill {
    background-color: hsl(var(--hue), 10%, 20%);
  }
}

/* Animations */
@keyframes fadeSlideIn {
  from {
    opacity: 0;
    transform: translateY(33%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes sdo {
  to {
    stroke-dashoffset: 0;
  }
}

/* Beyond mobile */
@media (min-width: 768px) {
  .modal__actions--center {
    justify-content: center;
    margin-left: -4.125em;
  }
  .modal__body {
    flex-direction: row;
    align-items: center;
  }
  .modal__button {
    width: auto;
  }
  .modal__button + .modal__button {
    margin-top: 0;
    margin-left: 1.5rem;
  }
  .modal__file ~ .modal__button {
    margin-top: 0;
  }
  .modal__file ~ .modal__close-button {
    margin-right: 1.5rem;
  }
  .modal__progress {
    margin-right: 2em;
  }
  .modal__progress + .modal__button {
    margin-top: 0;
  }
  .modal__col + .modal__col {
    margin-top: 0;
    margin-left: 1.875em;
  }
  .modal__title {
    text-align: left;
  }
}
</style>
